#!/usr/bin/python

import cgi
import logging

from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

from hips import datastore
from hips import serialization
from hips import translator


class QueryResult(object):

  def __init__(self, *query_result_list):
    self.model_list = []
    for query_result in query_result_list:
      for model in query_result:
        self.model_list.append(model)

  def All(self):
    return self.model_list

  def OfType(self, t):
    return [m for m in self.model_list if isinstance(m, t)]


# Functions
# --------------------------------


def BuildQuery(query, request_map):
  """Fills out a query template and executes the query.

  Args:
    query: Query template. The query must be a valid GQL query with
        possible templates for :login, :institution, :doctor, or :schedule.
    request_map: Map of keys for login, institution, doctor, or schedule.
        The query template is filled out with this map. Any template values
        that the query template refers to must be present in the map.

  Returns:
    query, query_params.
  """
  def CreateKey(maybe_key):
    if isinstance(maybe_key, db.Key):
      return maybe_key
    else:
      return db.Key(maybe_key)

  def AddArgIfNeeded(query, arg_name, params, request):
    if (":%s" % arg_name) in query:
      params[arg_name] = CreateKey(request.get(arg_name))

  query_params = {}
  AddArgIfNeeded(query, "login", query_params, request_map)
  AddArgIfNeeded(query, "institution", query_params, request_map)
  AddArgIfNeeded(query, "doctor", query_params, request_map)
  AddArgIfNeeded(query, "schedule", query_params, request_map)
  AddArgIfNeeded(query, "solution", query_params, request_map)
  if request_map.get("key"):
    if "WHERE" not in query:
      query += " WHERE "
    else:
      query += " AND "
    query += "__key__ = :key"
    query_params["key"] = CreateKey(request_map.get("key"))
  return query, query_params


def QueryForRawModels(query, request_map):
  """Queries for models based on context.

  Args:
    query: Query template. The query must be a valid GQL query with
        possible templates for :login, :institution, :doctor, or :schedule.
    request_map: Map of keys for login, institution, doctor, or schedule.
        The query template is filled out with this map. Any template values
        that the query template refers to must be present in the map.

  Returns:
    List of matching models.
  """
  query, query_params = BuildQuery(query, request_map)
  logging.info("Query: %s" % query)
  if query_params:
    model_list = db.GqlQuery(query, **query_params)
  else:
    model_list = db.GqlQuery(query)
  return model_list


def QueryForModels(query, request_map):
  """Queries for models based on context.

  Args:
    query: Query template. The query must be a valid GQL query with
        possible templates for :login, :institution, :doctor, or :schedule.
    request_map: Map of keys for login, institution, doctor, or schedule.
        The query template is filled out with this map. Any template values
        that the query template refers to must be present in the map.

  Returns:
    List of matching models.
  """
  return QueryResult(QueryForRawModels(query, request_map))


def QueryForInstitution(institution_key):
  key_map = {"key": institution_key}
  institution_model = QueryForRawModels(
      "SELECT * FROM Institution", key_map)
  # TODO Ordering
  key_map = {"institution": institution_key}
  doctors = QueryForRawModels(
      "SELECT * FROM Doctor WHERE institution = :institution",
      key_map)
  schedules = QueryForRawModels(
      "SELECT * FROM Schedule WHERE institution = :institution",
      key_map)
  return QueryResult(institution_model, doctors, schedules)


def QueryForSchedule(schedule_key):
  key_map = {"key": schedule_key}
  schedule_model = QueryForRawModels(
      "SELECT * FROM Schedule", key_map)
  # TODO Ordering of blocks
  key_map = {"schedule": schedule_key}
  schedule_blocks = QueryForRawModels(
      "SELECT * FROM ScheduleBlock WHERE schedule = :schedule",
      key_map)
  schedule_solutions = QueryForRawModels(
      "SELECT * FROM ScheduleSolution WHERE schedule = :schedule",
      key_map)
  constraints = QueryForRawModels(
      "SELECT * FROM DoctorConstraint WHERE schedule = :schedule",
      key_map)
  return QueryResult(schedule_model, schedule_blocks,
      schedule_solutions, constraints)


def QueryForScheduleSolution(solution_key):
  key_map = {"key": solution_key}
  solution_model = QueryForRawModels(
      "SELECT * FROM ScheduleSolution", key_map)
  # TODO Ordering of blocks
  key_map = {"solution": solution_key}
  solution_blocks = QueryForRawModels(
      "SELECT * FROM ScheduleSolutionBlock WHERE solution = :solution",
      key_map)
  return QueryResult(solution_model, solution_blocks)
